package com.livestocksolutions;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

import com.livestocksolutions.util.UpdateHistory;

/* -- Version History --
 *  1.0 : 11 July 2012 - Initial version.
 *  
 *  1.1 : 11 July 2012 - Changed contacts from type ContactDetail[] to ArrayList<ContactDetail>
 *
 *	1.2 : 11 July 2012 - Moved String name, getName() and setName(String) from Farm and Business Class to Owner Class
 *
 *  1.3 : 16 July 2012 - Class now implements Serializable
 *  
 *  1.4 : 05 September 2012 - Added private long modifiedTime, private boolean modified and private final long id
 *                       public long getID(), public boolean getModified() and public long getModifiedDate()
 *                       
 *  1.5 : 24 September 2012 - Added methods and attributes to track changes. Removed modified attributes introduced
 *                       in previous version.
 *                       Serialisation method no longer requires MedicalHistory implementing Serializable.
 */

/**
 * This class contains basic detail for a contact
 * @author Adam Parr
 * @version 1.5
 * 
 * @.modified 24 September 2012
 */
public class Owner{
	/**
	 * Keep track of any changes of this Owner.
	 */
	protected HashMap<String,UpdateHistory> update = new HashMap<String,UpdateHistory>();

	/**
	 * Unique ID that identifies this Owner
	 */
	protected final long id;
	/**
	 * Contact details of this Owner
	 */
	protected HashMap<Long,ContactDetail> contacts = new HashMap<Long,ContactDetail>();
	/**
	 * Name Associated with this Owner
	 */
	protected String name;
	
	/**
	 * Constructor for creating an Owner
	 */
	public Owner(){
		long tempID = Calendar.getInstance().getTimeInMillis();
		while(LivestockSolutions.getOwnerByID(tempID)!=null){
			// Just in case multiple items of this type are made in the same millisecond
			tempID++;
		}
		id=tempID;
	}
	
	/**
	 * Constructor
	 * @param id The id of the previous version of this Owner Object
	 */
	protected Owner(long id) {
		this.id=id;
	}

	/**
	 * Get all of the contact details for this Owner
	 * @return An array of ContactDetail that lists contact methods to contact this owner
	 */
	public ContactDetail[] getContacts(){
		return contacts.values().toArray(new ContactDetail[]{});
	}
	
	/**
	 * Get the name associated with this Owner
	 * @return Name of this Owner
	 */
	public String getName(){
		return name;
	}
	
	/**
	 * Set the name associated with this Owner
	 * @param name new name to give this Owner
	 */
	public void setName(String name){
		this.name=name.trim();
		
		update.put("NAME", new UpdateHistory(name));
	}
	
	/**
	 * Adds a contact to this Owner
	 * @param type The type of contact this is such as "home", "work" and so on.
	 * @param value the contact details this can be either a phone number, or an address
	 * 
	 * @deprecated This method does nothing, use {@link #addContact(ContactDetail)} instead.
	 */
	@Deprecated
	public void addContact(String type, String value){
		/*TODO test String value to see if a phone number or an address
		 * 212 51st St Town x 3029
		 * 13 6453 5066
		 * 
		 * top should create a:
		 * new ContactAddress();
		 *   .setStreet("212 51st St");
		 *   .setTown("Town x"); // If a state is provided should that be given here too?
		 *   .setPostcode("3029");
		 *   
		 * bottom list should create a:
		 * new ContactPhone();
		 *   .setNumber("13 6453 5066");
		 */
	}
	
	/**
	 * Adds the given ContactDetail to this Owner
	 * @param contact the new contact details to add
	 * @return <code>true</code> if given contacts was added to this Owner, <code>false</code> otherwise
	 */
	public boolean addContact(ContactDetail contact){
		if(contact.getID()==0){
			// Adding a new Contact
			long newID = new Date().getTime();
			while(contacts.containsKey(newID)){
				newID++;
			}
			contact.setID(newID);
		}
		if (!contacts.containsKey(contact.getID())){
			UpdateHistory contactList = update.get("CONTACT")!=null?update.get("CONTACT"):new UpdateHistory();
			contactList.setValues(String.valueOf(contact.getID()),new UpdateHistory("true"));
			update.put("CONTACT", contactList);
			contacts.put(contact.getID(),contact);
			return true;
		}
		return false;
	}
	
	/**
	 * Removes a contact from the array at index position.
	 * @param index the position in the array to remove that contact.
	 * @throws IndexOutOfBoundsException if index is greater than the number of contacts.
	 */
	public void removeContact(int index) throws IndexOutOfBoundsException{
		UpdateHistory contactList = update.get("CONTACT")!=null?update.get("CONTACT"):new UpdateHistory();
		contactList.setValues(String.valueOf(contacts.get(index).getID()),new UpdateHistory("false"));
		update.put("CONTACT", contactList);
		contacts.remove(index);
	}
	
	/**
	 * Get the unique id of this Owner
	 * @return the id of this Owner
	 */
	public long getID(){
		return id;
	}
	
	/**
	 * Changes the type of this Owner.
	 * @param subClass The Class of the new Owner that will replace this
	 * @return The new instance of this object reflecting the type change.
	 */
	public Owner changeType(Class<? extends Owner> subClass){
		try {
			// Change this Owner to a sub class version
			Owner o = subClass.getDeclaredConstructor(long.class).newInstance(id);
			o.name=name;
			o.contacts=contacts;
			o.update=update;
			return o;
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes Owner that represents this Object in a different version.
	 * @return <tt>true</tt> if changes is not null and changes does not equal this object, <tt>false</tt> otherwise.
	 */
	public boolean update(Owner changes){
		if(changes!=null && this!=changes){
			if(updateBefore("NAME",changes)){
				name=changes.update.get("NAME").getValue();
				update.put("NAME", new UpdateHistory(name));
			}
			
			if(changes.update.get("CONTACT")!=null){
				for(String key : changes.update.get("CONTACT").toKeyArray()){
					if(update.get("CONTACT")==null || update.get("CONTACT").getValue(key)==null || !update.get("CONTACT").getValue(key).getDate().after(changes.update.get("CONTACT").getValue(key).getDate())){
						UpdateHistory contactList = update.get("CONTACT")!=null?update.get("CONTACT"):new UpdateHistory();
						if(!changes.update.get("CONTACT").getValue(key).equals("false")){
							// Remove
							contacts.remove(changes.contacts.get(Long.parseLong(key)));
							contactList.setValues(key,new UpdateHistory("false"));
						}else{
							// Add
							contacts.put(Long.parseLong(key),changes.contacts.get(Long.parseLong(key)));
							contactList.setValues(key,new UpdateHistory("true"));
						}
						update.put("CONTACT", contactList);
					}
				}
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Checks to see if the modification date of the item at this position is before the changed value
	 * @param key The String key at which to compare the modification dates of the modified objects.
	 * @param changes The Owner that represents this Owner on another system.
	 * @return <tt>true</tt> if the old value was modified before the changed value. <tt>false</tt> otherwise.
	 */
	protected boolean updateBefore(String key,Owner changes){
		if(this.update.get(key)==null && changes.update.get(key)!=null){
			return true;
		}else if(changes.update.get(key)==null){
			return false;
		}
		return !this.update.get(key).getDate().after(changes.update.get(key).getDate());
	}
}
